LimiterA peak limiter that keeps the output around a ceiling, built on the native Web Audio DynamicsCompressorNode (hard knee, 20:1 ratio, 1 ms attack). Typically placed at the end of a Master chain, so many sounds playing at once do not clip.
Extends Effector. Limiter does not use an AudioWorklet, so it does not require WebAssembly.
Every SpatialAudioRenderer2D and SpatialAudioRenderer3D attaches a Limiter to its master channel by default (see renderer.limiter).
input -> input gain -> DynamicsCompressorNode -> makeup compensation -> soft clipper -> output
import { Limiter } from "@fluex/fluexgl-dsp";
const limiter = new Limiter({ ceiling: -1, release: 0.1 });
audioDevice.getMasterChannel().attachEffect(limiter);
const renderer = new SpatialAudioRenderer3D(audioDevice, {
limiter: { ceiling: -3, release: 0.15 }
});
renderer.limiter?.setInputGain(3);
// Or disable it:
const noLimiter = new SpatialAudioRenderer2D(audioDevice, { limiter: false });
new Limiter(options?: Partial<LimiterOptions>): Limiter;
options?: Partial<LimiterOptions>
Optional limiter configuration. Every field is optional, falls back to the class defaults below, and is clamped to its valid range.label: string | nullCustom label for this effector. Defaults to "Limiter".
name: stringInternal name for this effector. Defaults to "Limiter".
ceiling: numberMaximum output level (dB). Between -24 and 0. Defaults to -1.
release: numberTime (seconds) for the limiter to recover. Between 0.01 and 1. Defaults to 0.1.
inputGain: numberGain (dB) applied before limiting, to drive the signal into the limiter. Between -24 and 24. Defaults to 0.
inputGainNode: GainNode | nullApplies inputGain. null until the effect is attached.
compressorNode: DynamicsCompressorNode | nullThe native compressor node that does the limiting. null until the effect is attached.
outputGainNode: GainNode | nullCompensates the automatic makeup gain of the compressor node (see Notes). null until the effect is attached.
clipperNode: WaveShaperNode | nullA soft clipper after the compressor (see Notes). This is the output node. null until the effect is attached.
initializeOnAttachment(context: AudioContext): Promise<void>Creates the input gain, compressor and output gain nodes with the current option values. When the effect is attached again on the same AudioContext, the existing nodes are reused.
context: AudioContextPromise<void>returnOptionsAsObject(): LimiterOptionsReturns a plain object snapshot of the current ceiling, release and inputGain values.
setCeiling(ceiling: number): booleanUpdates ceiling (clamped between -24 and 0) and the makeup compensation, with a short ramp.
ceiling: numberboolean - true when applied to the audio nodes, false when the effect is not attached yet (the value is used once attached).setRelease(release: number): booleanUpdates release (clamped between 0.01 and 1), with a short ramp.
release: numberboolean - true when applied to the audio node, false when the effect is not attached yet.setInputGain(inputGain: number): booleanUpdates inputGain (clamped between -24 and 24), with a short ramp.
inputGain: numberboolean - true when applied to the audio node, false when the effect is not attached yet.get reduction(): numberCurrent gain reduction in dB (0 or negative). Useful for metering. 0 while the effect is not attached.
get inputNode(): AudioNode | nullReturns inputGainNode. See Effector.
get outputNode(): AudioNode | nullReturns clipperNode. See Effector.
Limiter does not use an AudioWorklet processor, so it does not dispatch the processor events of Effector.
The Web Audio specification adds an automatic makeup gain inside every DynamicsCompressorNode: (1 / fullRangeGain) ^ 0.6, where fullRangeGain is the curve's gain for a 0 dBFS input. For a ceiling of -1 dB this is about +0.57 dB, which would push the output above the ceiling. Limiter undoes this with outputGainNode.
The compressor does the actual limiting, but its ratio is 20:1 rather than infinite and its attack is not instant, so loud peaks would still pass slightly above the ceiling (about +0.6 dB for a +6 dBFS input at a -1 dB ceiling). A WaveShaperNode at the end catches those: it is exactly linear below 90% of the ceiling and bends the rest smoothly (tanh) towards the ceiling. The sample peak therefore never exceeds the ceiling. The clipper only adds distortion while it is actually catching an overshoot.
It does not measure inter-sample (true) peaks. A true-peak limiter with a configurable lookahead would require a WebAssembly implementation.
The makeup compensation follows the DynamicsCompressorKernel of Chromium (also used by Firefox).
Master processes effects in the order they were attached, and does not offer a way to reorder them. Effects attached after the limiter come after it and can raise the level again.